const {Board, Role} = require('board-game');
const extend = require('extend');

const {game: gameConfig} = require('./config');
const Item = require('./item');
const Runner = require('./runner');

class RunningBoard extends Board {
    constructor(game) {
        super(game);

        this.runners = this.initRunners();
        this.cycle = this.initCycle();
        this.start = +Date.now();
    }
    initCycle() {
        let multi = 1 + gameConfig.grid.space;
        let cycle = new Array(this.runners.length * multi).fill(true);
        for (let i = 0; i < this.runners.length; i++) {
            let position = i * multi;
            cycle[position] = this.runners[i];
            cycle[position].position = position;
        }
        return cycle;
    }
    initRunners() {
        return this.game.groups.flatMap(group => group.roles).map(role => new Runner(this, role));
    }
    putItem(item) {
        let indices = [];
        if (this.cycle.filter(node => node instanceof Item).length < this.cycle.filter(node => node instanceof Runner && node.level > 0).length) {
            for (let i = 0; i < this.cycle.length; i++) {
                if (this.cycle[i] === true) {
                    indices.push(i);
                }
            }
            let index = indices[Math.floor(indices.length * Math.random())];
            this.cycle[index] = item;
        }
    }
    isWin(groupIndex) {
        if (this.cycle.filter(node => node instanceof Runner).length <= 1) {
            return this.cycle.some(node => node instanceof Runner && node.role === this.groups[groupIndex].roles[0]);
        } else {
            return false;
        }
    }
    doNext(index, nextIndex, runner, last = true) {
        this.cycle[index] = last;
        this.cycle[nextIndex] = runner;
        this.resetPositions();
    }
    resetPositions() {
        for (let position = 0, i = 0; i < this.cycle.length; i++) {
            let node = this.cycle[i];
            if (node !== false) {
                if (node instanceof Runner) {
                    node.position = position + (node.position % 1);
                }
                position++;
            }
        }
    }
    indexOf(runner) {
        return this.cycle.indexOf(runner);
    }
    get(index) {
        return this.cycle[index];
    }
    count(predicate) {
        return this.cycle.filter(predicate).length;
    }
    getRunner(role) {
        return this.runners.find(runner => runner.role === role);
    }
    buff(runner, type, keep) {
        runner.buff(type, keep);
    }
    checkBuff(runner, buff) {
        runner.checkBuff(buff);
    }
    offBuff(runner, buff) {
        runner.offBuff(buff);
    }
    rebound(runner) {
        runner.rebound();
    }

    get data() {
        return extend(true, super.data, {
            cycle: this.cycle.map(node => node instanceof Item ? node.type : node instanceof Runner ? node.data : node),
            runners: this.runners.map(runner => runner.data),
        });
    }
}

module.exports = RunningBoard;
